package com.kcht.itsed.mws.alefacade.stdfacade.impl;

import com.kcht.itsed.mws.alefacade.exceptions.*;
import com.kcht.itsed.mws.alefacade.stdfacade.ALE;
import com.kcht.itsed.mws.aleframework.intf.IActiveSpecContainer;
import com.kcht.itsed.mws.aleframework.intf.IActiveSpecScheduler;
import com.kcht.itsed.mws.aleframework.intf.ILogicalReaderContainer;
import com.kcht.itsed.mws.alemodel.ECReports;
import com.kcht.itsed.mws.alemodel.ECSpec;
import com.kcht.itsed.mws.extendmodel.jpa.EventCycleSpecEntity;
import com.kcht.itsed.mws.extendmodel.jpa.SubscriberEntity;
import com.kcht.itsed.mws.persist.dao.IEventCycleDao;
import com.kcht.itsed.mws.persist.dao.ISubscriberDao;
import com.kcht.itsed.mws.subscribescheduler.facade.IPollManager;
import com.kcht.itsed.mws.subscribescheduler.facade.ISubscriberDeployer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class ALE_IMPL implements ALE {

    @Autowired
    IEventCycleDao ecDao;
    @Autowired
    ISubscriberDao scDao;

    @Autowired
    IActiveSpecContainer activeSpecContainer;
    @Autowired
    ISubscriberDeployer subscriberDeployer;
    @Autowired
    IActiveSpecScheduler activeSpecScheduler;

    @Autowired
    ILogicalReaderContainer lrContainer;

    @Autowired
    IPollManager pollManager;

    @Override
    public void define(String specName, ECSpec spec) throws DuplicateNameException, ECSpecValidationException {
        Optional<EventCycleSpecEntity> existedEntity = ecDao.findBySpecName(specName);
        if (existedEntity.isPresent()) throw new DuplicateNameException("EC spec [" + specName + "] already existed.");
        //TODO 校验规则

        EventCycleSpecEntity entity = new EventCycleSpecEntity();
        entity.setEcSpecName(specName);
        entity.setEcSpec(spec);
        ecDao.insert(entity);
    }

    @Override
    public void undefine(String specName) throws NoSuchNameException {
        //TODO 检查在用情况，ALE协议未定义InUse异常，这里拟采用ImplementationException来封装
        Optional<EventCycleSpecEntity> existedEntity = ecDao.findBySpecName(specName);
        existedEntity.orElseThrow(() -> new NoSuchNameException("No such EC Spec[" + specName + "]"));

        if (!scDao.isAssociatedEc(existedEntity.get().getId()))
            throw new ImplementationException("EC Spec[" + specName + "] has subscribers and cannot be undefined.");
        ecDao.dropEventCycleSpec(specName);
    }

    @Override
    public ECSpec getECSpec(String specName) throws NoSuchNameException {
        Optional<EventCycleSpecEntity> existedEntity = ecDao.findBySpecName(specName);
        existedEntity.orElseThrow(() -> new NoSuchNameException("No such EC Spec[" + specName + "]"));
        return existedEntity.get().getEcSpec();
    }

    @Override
    public List<String> getECSpecNames() {
        return ecDao.queryEventCycleSpec();
    }

    @Override
    public void subscribe(String specName, String notificationURI)
            throws NoSuchNameException, InvalidURIException, DuplicateSubscriptionException {
        //TODO 检查订阅者URL格式是否正确

        Optional<EventCycleSpecEntity> ec = ecDao.findBySpecName(specName);
        ec.orElseThrow(() -> new NoSuchNameException("No such EC spec[" + specName + "]."));

        EventCycleSpecEntity ecEntity = ec.get();

        Set<String> existedSubscribers = scDao.loadAssociatedSubscriber(ecEntity.getId()).stream().map(SubscriberEntity::getSubscriberUrl).collect(Collectors.toSet());
        if (existedSubscribers.contains(notificationURI))
            throw new DuplicateSubscriptionException("Subscriber[" + notificationURI + "] already existed for EC spec[" + specName + "]");

        if (existedSubscribers.isEmpty()) {
            //原规则未被订阅，那么添加该规则到活动规则中
            activeSpecContainer.addActiveEC(specName, ecEntity.getEcSpec());
            ecEntity.getEcSpec().getLogicalReaders().getLogicalReader().forEach(lrName -> {
                lrContainer.bindActiveEcSpecToLr(specName, lrName);
            });
            activeSpecScheduler.shuffleSpec();
        }

        //保存订阅者
        SubscriberEntity se = new SubscriberEntity();
        se.setSubscriberUrl(notificationURI);
        se.setRelatedSpec(specName);
        se.setRelatedSpecType("EC");
        se.setEventCycleSpecId(ecEntity.getId());
        scDao.insert(se);
        subscriberDeployer.deployEcSubscriber(specName, notificationURI);

    }

    @Override
    public void unsubscribe(String specName, String notificationURI)
            throws NoSuchNameException, NoSuchSubscriberException, InvalidURIException {
        //TODO 检查订阅者URL格式是否正确

        Optional<EventCycleSpecEntity> ec = ecDao.findBySpecName(specName);
        ec.orElseThrow(() -> new NoSuchNameException("No such EC spec[" + specName + "]."));

        EventCycleSpecEntity ecEntity = ec.get();
        Set<SubscriberEntity> subscriberEntities = scDao.loadAssociatedSubscriber(ecEntity.getId());
        Set<String> existedSubscribers = subscriberEntities.stream().map(SubscriberEntity::getSubscriberUrl).collect(Collectors.toSet());
        if (!existedSubscribers.contains(notificationURI))
            throw new NoSuchSubscriberException("Subscriber[" + notificationURI + "] not existed for EC spec[" + specName + "]");
        subscriberEntities.removeIf(p -> {
            if (p.getSubscriberUrl().equals(notificationURI)){
				scDao.deleteById(p.getId());
            	return true;
			}
            return false;
        });
        subscriberDeployer.undeployEcSubscriber(specName, notificationURI);

        if (subscriberEntities.isEmpty()) {
            //无订阅者了，那么该EC规则移出活动
            lrContainer.removeLrEcRelation(specName);
            activeSpecContainer.removeEC(specName);
            activeSpecScheduler.shuffleSpec();
        }
    }

    @Override
    public ECReports poll(String specName) throws NoSuchNameException {

        String notificationURI = "poll://ec/" + specName;
        Optional<EventCycleSpecEntity> ec = ecDao.findBySpecName(specName);
        ec.orElseThrow(() -> new NoSuchNameException("No such EC spec[" + specName + "]."));

        EventCycleSpecEntity ecEntity = ec.get();
		Set<String> existedSubscribers = scDao.loadAssociatedSubscriber(ecEntity.getId()).stream().map(SubscriberEntity::getSubscriberUrl).collect(Collectors.toSet());

        if (existedSubscribers.isEmpty()) {
            //原规则未被订阅，那么添加该规则到活动规则中
            activeSpecContainer.addActiveEC(specName, ecEntity.getEcSpec());
            ecEntity.getEcSpec().getLogicalReaders().getLogicalReader().forEach(lrName -> {
                lrContainer.bindActiveEcSpecToLr(specName, lrName);
            });
            activeSpecScheduler.shuffleSpec();
        }
//		//以上完成订阅，下面等待一次结果返回
        try {
            subscriberDeployer.deployEcSubscriber(specName, notificationURI);
            return pollManager.waitForECPollResult(specName);
        } catch (Exception e) {
            throw new ImplementationException("Poll ECSpec[" + specName + "] failed", e);
        } finally {
            subscriberDeployer.undeployEcSubscriber(specName, notificationURI);
            if (existedSubscribers.isEmpty()) {
                //无订阅者了，那么该EC规则移出活动
                lrContainer.removeLrEcRelation(specName);
                activeSpecContainer.removeEC(specName);
                activeSpecScheduler.shuffleSpec();
            }
        }


        //保守实现，应该是用不上了
//		try {
//			subscribe(specName, notificationURI);
//			ECReports resultReport = pollManager.waitForECPollResult(specName);
//			unsubscribe(specName, notificationURI);
//			if(resultReport == null)throw new ImplementationException("Poll spec["+specName+"] failed!");
//			return resultReport ;
//		} catch (InvalidURIException | DuplicateSubscriptionException | NoSuchSubscriberException e) {
//			e.printStackTrace();
//		}
//		throw new ImplementationException("Poll spec["+specName+"] failed!");

    }

    @Override
    public ECReports immediate(ECSpec spec) throws ECSpecValidationException {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public List<String> getSubscribers(String specName) throws NoSuchNameException {
        Optional<EventCycleSpecEntity> ec = ecDao.findBySpecName(specName);
        ec.orElseThrow(() -> new NoSuchNameException("No such EC spec[" + specName + "]."));
        return  scDao.loadAssociatedSubscriber(ec.get().getId()).stream().map(SubscriberEntity::getSubscriberUrl).collect(Collectors.toList());
    }

}
